package ar.com.gals.repository;

import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashSet;
import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.Hibernate;
import org.hibernate.Session;
import org.hibernate.criterion.Conjunction;
import org.hibernate.criterion.CriteriaSpecification;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;

import ar.com.gals.domain.Articulo;
import ar.com.gals.domain.Equivalencia;
import ar.com.gals.domain.Observacion;
import ar.com.gals.domain.TipoObservacion;
import ar.com.gals.filter.ArticuloFiltro;


public class ArticuloRepository extends AbstractRepository<Articulo> {

	public ArticuloRepository(Session session) {
		super(session, Articulo.class);
	}
	
	
	@SuppressWarnings("unchecked")
	@Override
	public Articulo getById(int id) {
		
		Criteria criteria = this.session.createCriteria(domain)
				.add(Restrictions.idEq(id))
				.add(Restrictions.isNull("fechaDelete"))
				.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY);
		List<Articulo> list = (List<Articulo>) criteria.list();
		if(list.isEmpty()) return null;
		Articulo articulo = (Articulo) list.get(0);
		
		this.initializeObservaciones(articulo);
		this.initializeEquivalencias(articulo);

		return articulo;
	}

	
	@SuppressWarnings("unchecked")
	public List<Articulo> getAll(ArticuloFiltro filtro, boolean fullLoad) {
		
		/* SI ES BUSQUEDA GENERICA DEVUELVO PRIMEROS 20 */
		if(filtro.isEmpty())
			return this.getAllOrderByLimit("codigo", 20);
		
		Criteria criteria = this.session.createCriteria(domain);
		
		/* CODIGO */
		if(filtro.getCodigo() != null && !filtro.getCodigo().isEmpty())
			criteria.add(Restrictions.ilike("codigo", "%"+filtro.getCodigo()+"%"));
		
		/* DESCRIPCION */
		if(filtro.getDescripcion() != null && !filtro.getDescripcion().isEmpty()) {
			Conjunction conjuncion = Restrictions.conjunction();
			for(String value : filtro.getDescripcion().split(" ")) {
				if(!value.isEmpty())
					conjuncion.add(Restrictions.ilike("descripcion", "%"+value+"%"));
			}
			criteria.add(conjuncion);
		}
		
		/* MARCA */
		if(filtro.getMarca() != null) {
			Criteria criteriaMarca = criteria.createCriteria("marca");
			criteriaMarca.add(Restrictions.idEq(filtro.getMarca()));
		}
		
		/* RUBRO */
		if(filtro.getRubro() != null) {
			Criteria criteriaRubro = criteria.createCriteria("rubro");
			criteriaRubro.add(Restrictions.idEq(filtro.getRubro()));
		}
		
		/* OBSERVACION */
		if(filtro.getTipoObservacion() != null) {
			
			Criteria criteriaObservacion = criteria.createCriteria("observaciones");
			Criteria criteriaTipoObservacion = criteriaObservacion.createCriteria("tipoObservacion");
			criteriaTipoObservacion.add(Restrictions.idEq(filtro.getTipoObservacion()));

			TipoObservacion tipoObservacion = new TipoObservacionRepository(this.session).getById(filtro.getTipoObservacion());
			
			switch (tipoObservacion.getUnidadMedida().getTipoDato()) {
				case ALFANUMERICO:
					criteriaObservacion.add(Restrictions.ilike("valor", "%"+filtro.getObservacion1()+"%"));
					break;
	
				case NUMERICO:
					switch (filtro.getOperador()) {
						case IGUAL:
							criteriaObservacion.add(
								Restrictions.sqlRestriction(
									"CASE WHEN isnumeric({alias}.valor) THEN {alias}.valor::NUMERIC = " + filtro.getObservacion1() + " ELSE false END"
								)
							);
							break;
							
						case MENOR:
							criteriaObservacion.add(
								Restrictions.sqlRestriction(
									"CASE WHEN isnumeric({alias}.valor) THEN {alias}.valor::NUMERIC <= " + filtro.getObservacion1() + " ELSE false END"
								)
							);
							break;	
						
						case MAYOR:
							criteriaObservacion.add(
								Restrictions.sqlRestriction(
									"CASE WHEN isnumeric({alias}.valor) THEN {alias}.valor::NUMERIC >= " + filtro.getObservacion1() + " ELSE false END"
								)
							);
							break;
							
						case ENTRE:
							criteriaObservacion.add(
								Restrictions.sqlRestriction(
									"CASE WHEN isnumeric({alias}.valor) THEN {alias}.valor::NUMERIC BETWEEN " + filtro.getObservacion1() + " AND " + filtro.getObservacion2() + " ELSE false END"
								)
							);
							break;
							
						default:
							break;
					}
					break;	
					
				default:
					break;
			}
		}
		
		/* EQUIVALENCIA */
		if(filtro.getTipoEquivalencia() != null) {
			Criteria criteriaEquivalencia = criteria.createCriteria("equivalencias");
			Criteria criteriaTipoEquivalencia = criteriaEquivalencia.createCriteria("tipoEquivalencia");
			criteriaTipoEquivalencia.add(Restrictions.idEq(filtro.getTipoEquivalencia()));
			criteriaEquivalencia.add(Restrictions.ilike("valor", "%"+filtro.getEquivalencia()+"%"));
		}
		
		criteria.add(Restrictions.isNull("fechaDelete"));
		criteria.addOrder(Order.asc("codigo"));
		criteria.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY);
		
		List<Articulo> list = (List<Articulo>) criteria.list();
		
		if(fullLoad) {
			for(Articulo articulo : list) {
				this.initializeObservaciones(articulo);
				this.initializeEquivalencias(articulo);
			}
		}
		
		return list;
	}
	
	
	private void initializeObservaciones(Articulo articulo) {
		
		Hibernate.initialize(articulo.getObservaciones());
		
		for(Observacion observacion : new LinkedHashSet<Observacion>(articulo.getObservaciones()))
			if(observacion.getTipoObservacion().getFechaDelete() != null)
				articulo.getObservaciones().remove(observacion);
		
		Collections.sort(articulo.getObservaciones(), new Comparator<Observacion>() {
			@Override
	        public int compare(Observacion observacion1, Observacion observacion2)
	        {
	        	return (observacion1.getTipoObservacion().getNombre()).compareTo(observacion2.getTipoObservacion().getNombre());
	        }
		});
	}
	
	
	private void initializeEquivalencias(Articulo articulo) {
		
		Hibernate.initialize(articulo.getEquivalencias());
			
		for(Equivalencia equivalencia : new LinkedHashSet<Equivalencia>(articulo.getEquivalencias()))
			if(equivalencia.getTipoEquivalencia().getFechaDelete() != null)
				articulo.getEquivalencias().remove(equivalencia);
		
		Collections.sort(articulo.getEquivalencias(), new Comparator<Equivalencia>() {
			@Override
	        public int compare(Equivalencia equivalencia1, Equivalencia equivalencia2)
	        {
	        	return (equivalencia1.getTipoEquivalencia().getNombre()).compareTo(equivalencia2.getTipoEquivalencia().getNombre());
	        }
		});
	}
}
